/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.yandex.collections;

import java.util.Arrays;

/**
 *
 * @author vlad
 */
public class MyArrayList implements MyList {

    private static final int DEF_SIZE = 10;
    private static final int EXPAND_COEF = 3;
    
    private Object[] array;
    private int size;

    public MyArrayList() {
        array = new Object[DEF_SIZE];
        size = 0;
    }

    public MyArrayList(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("Capacity must be non-negative"
                    + " value");
        }
        array = new Object[initialCapacity];
        size = 0;
    }

    public MyArrayList(Object[] c) {
        array = c.clone();
        size = array.length;
    }

    private boolean inBounds(int index) {
        return index < size && index >= 0;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        Object[] sufficient = new Object[size];
        System.arraycopy(array, 0, sufficient, 0, size);
        hash = 97 * hash + Arrays.deepHashCode(sufficient);
        return hash;
    }   

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        MyArrayList other = (MyArrayList) obj;
        if (this.size != other.size) {
            return false;
        }
        if (this.hashCode() != other.hashCode()) {
            return false;
        }
        for (int i = 0; i < size; i++) {
            if (this.array[i] != other.array[i]) {
                return false;
            }
        }
        return true;
    }

    public void ensureCapacity(int minCapacity) {
        if (array.length < minCapacity) {
            Object[] newArray = new Object[minCapacity];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }
    }

    private void addSpace() {
        if (size == array.length) {
            ensureCapacity(size * EXPAND_COEF / 2 + 1);
        }
    }

    private void shrink() {
        if (size < array.length) {
            Object[] newArray = new Object[size];
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
        }
    }

    public int getCapacity() {
        return array.length;
    }

    @Override
    public String toString() {
        return Arrays.toString(this.toArray());
    }

    @Override
    public void add(Object e) {
        addSpace();
        array[size] = e;
        size++;
    }

    @Override
    public void add(int index, Object e) {
        if (size == index) {
            this.add(e);
        } else {
            if (!inBounds(index)) {
                throw new IndexOutOfBoundsException("MyArrayList boundaries"
                        + " have been violated");
            }
            addSpace();
            System.arraycopy(array, index, array, index + 1, size - index);
            array[index] = e;
            size++;
        }
    }

    @Override
    public void addAll(Object[] c) {
        ensureCapacity(size + c.length);
        System.arraycopy(c, 0, array, size, c.length);
        size += c.length;
    }

    @Override
    public void addAll(int index, Object[] c) {
        if (size == index) {
            this.addAll(c);
        } else {
            if (!inBounds(index)) {
                throw new IndexOutOfBoundsException("MyArrayList boundaries"
                        + " have been violated");
            }
            Object[] temp = new Object[size - index];
            System.arraycopy(array, index, temp, 0, size - index);
            ensureCapacity(size + c.length);
            System.arraycopy(temp, 0, array, index + c.length, size - index);
            System.arraycopy(c, 0, array, index, c.length);
            size += c.length;
        }
    }

    @Override
    public Object get(int index) {
        if (!inBounds(index)) {
            throw new IndexOutOfBoundsException("MyArrayList boundaries"
                    + " have been violated");
        }
        return array[index];
    }

    @Override
    public Object remove(int index) {
        if (!inBounds(index)) {
            throw new IndexOutOfBoundsException("MyArrayList boundaries"
                    + " have been violated");
        }
        Object popped = array[index];
        System.arraycopy(array, index + 1, array, index, size - index - 1);
        size--;
        shrink();
        return popped;
    }

    @Override
    public void set(int index, Object e) {
        if (!inBounds(index)) {
            throw new IndexOutOfBoundsException("MyArrayList boundaries"
                    + " have been violated");
        }
        array[index] = e;
    }

    @Override
    public int indexOf(Object o) {
        int i = 0;
        while (i < size && !(array[i].equals(o))) {
            i++;
        }
        if (i == size) {
            return -1;
        }
        return i;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        array = new Object[DEF_SIZE];
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Object[] toArray() {
        Object[] arrcopy = new Object[size];
        System.arraycopy(array, 0, arrcopy, 0, size);
        return arrcopy;
    }

}
